﻿/*
COPYRIGHT NOTICE

Copyright (c) 2011, S James S Stapleton
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
	  this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
	  this list of conditions and the following disclaimer in the documentation
	  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Reflection;

namespace LocalL
{
    /// <summary>
    /// This is the localization engine. It can be interacted with in two ways.you can create new
    /// engines with "Localizer MyLoc = new Localizer(...)", and then access them using 
    /// MyLoc.FunctionName(). If "SOMETHING" cannot be translated, it is output as "{{SOMETHING}}".
    /// Strings starting with '@' are not tranlated, and printed out as-is. There are several abstract
    /// functions that need to be implemetned (see Localizer) to create a localizer. The Write...
    /// functions will automatically work.
    /// </summary>
    abstract public class ALocalizer : TextWriter
    {
        #region variables
        static public readonly int c_version_major = 0;
        static public readonly int c_version_minor = 1;
        static public readonly int c_version_build = 2;
        static public readonly string c_version_string = "LocalL "+c_version_major.ToString()+"."+c_version_minor.ToString()+"."+c_version_build.ToString()+" (alpha)";
        /// <summary>
        /// This contains the localizations used.
        /// </summary>
        protected internal Dictionary<string, Localization> m_localizations = new Dictionary<string, Localization>();
        /// <summary>
        /// The default localization
        /// </summary>
        protected internal string m_default_localization = null;
        /// <summary>
        /// Get or set the default language to localize to.
        /// </summary>
        public string DefaultLocalization
        {
            get
            {
                return this.m_default_localization;
            }
            set
            {
                if(!this.m_localizations.ContainsKey(value))
                {
                    throw new ArgumentException("Localization '" + value + "' is not loaded, and cannot be set as the default localization.");
                }
                this.m_default_localization = value;
            }
        }
        /// <summary>
        /// Output is done to streams by default
        /// </summary>
        protected internal TextWriter m_output = null;
        /// <summary>
        /// Get/Set the stream this writes to by default.
        /// </summary>
        public TextWriter Output
        {
            get
            {
                return this.m_output;
            }
            set
            {
                this.m_output = value;
            }
        }
        /// <summary>
        /// THe 'do-not-translate' prefix
        /// </summary>
        protected internal string m_dont_translate = "@";
        /// <summary>
        /// Get/set the string that, if at the beginning of a string sent to the translator,
        /// is removed, and indicates the string isn't translated.
        /// </summary>
        public string DontTranslate
        {
            get
            {
                return m_dont_translate;
            }
            set
            {
                m_dont_translate = value;
            }
        }
        /// <summary>
        /// This is used to obtain the correct type localizer
        /// </summary>
        protected internal Dictionary<string, TypeLocalizers> m_type_localizers;
        /// <summary>
        /// This controls which how different types are localized.
        /// </summary>
        public Dictionary<string, TypeLocalizers> TypeLocalizers
        {
            get
            {
                return this.m_type_localizers;
            }
        }
        #endregion

        #region constructors
        /// <summary>
        /// create a new localization engine, without an output stream or dictionary set.
        /// </summary>
        public ALocalizer() : base()
        { }
        /// <summary>
        /// create a new localization engine, without a dictionary of translations.
        /// </summary>
        /// <param name="output">Output goes to this if destination is unspecified.</param>
        public ALocalizer(TextWriter output) : base()
        {
            this.m_output = output;
        }
        /// <summary>
        /// create a new localization engine, without a default output stream.
        /// </summary>
        /// <param name="localizations">The source file to contain string localization definitions.</param>
        public ALocalizer(params string[] localizations) : base()
        {
            this.Load(localizations);
        }
        /// <summary>
        /// create a new localization engine, without a default output stream.
        /// </summary>
        /// <param name="output">Output goes to this if destination is unspecified.</param>
        /// <param name="localizations">The source file to contain string localization definitions.</param>
        public ALocalizer(TextWriter output, params string[] localizations) : base()
        {
            this.m_output = output;
            this.Load(localizations);
        }
        #endregion

        #region load/save functions
        /// <summary>
        /// Read the configuration from a set of file. Each file MUST have one "Localization_ID=" key, stating
        /// the identifier of the localization (such as "Localization_ID=en_US" or "Localization_ID=ru"), and one
        /// "Localization_Name=" key, stating the friendly name for the localization (such as "English, US" or
        /// "русский (Russian)"). If a default translation isn't set, we try to translate it.
        /// </summary>
        /// <param name="files">The files to load</param>
        /// <returns>This is the errors reported (internationalized, see "Localization Keys.txt"</returns>
        abstract public string[][] Load(params string[] files);
        /// <summary>
        /// Save the current translations (but not loaded functions/classes)
        /// </summary>
        /// <param name="file">The file to store the cinfiguration</param>
        /// <param name="localization">The localization to save</param>
        abstract public void Save(string file, string localization);

        /// <summary>
        /// Loads a type localizer into the active translation set.
        /// </summary>
        /// <param name="key">They key this is assigned to</param>
        /// <param name="val">The assembly to load, in "assembly_file:class_name" format</param>
        /// <returns>Error information, null on success</returns>
        abstract public string[] LoadTypeLocalizer(string key, string val);
        #endregion

        #region Core Translation Functions
        /// <summary>
        /// This is a type translator
        /// </summary>
        /// <param name="o">The object to translate, can be any type</param>
        /// <returns>o.ToString()</returns>
        public static string ToStringTypeTranslator(object o)
        {
            return o.ToString();
        }
        protected internal static readonly LocalizeFunction c_default_translator = new LocalizeFunction(ToStringTypeTranslator);
        /// <summary>
        /// This gets a default type translator, which is used by the engine if no others are available.
        /// </summary>
        public static LocalizeFunction DefaultTranslator
        {
            get
            {
                return c_default_translator;
            }
        }
        /// <summary>
        /// Returns true if the string needs translated. If it does not, s should be output. S may be modified
        /// as this usually checks for a prefix string in S suggesting it doesn't need translated, and then removes
        /// that prefix string.
        /// </summary>
        /// <param name="s">The string to check</param>
        /// <returns>True if the string needs translated, otherwise false.</returns>
        abstract protected internal bool requires_translate(ref string s);
        /// <summary>
        /// Get a localization of an internationalized string
        /// </summary>
        /// <param name="val">The string to localize</param>
        /// <returns>The localized string, or the original string surrounded by "{{" and "}}" if no localization is available.</returns>
        abstract public string Translate(string val);
        /// <summary>
        /// As Translate, but without checking for the existence of a default localization.
        /// </summary>
        abstract protected internal string Translate_(string val);
        /// <summary>
        /// Get a localization of an internationalized string
        /// </summary>
        /// <param name="lang">The language to localize to</param>
        /// <param name="val">The string to localize</param>
        /// <returns>The localized string, or the original string surrounded by "{{" and "}}" if no localization is available.</returns>
        abstract public string Translate(string lang, string val);
        /// <summary>
        /// Get a localization of an internationalized string
        /// </summary>
        /// <param name="lang">The language to localize to</param>
        /// <param name="val">The string to localize</param>
        /// <returns>The localized string, or the original string surrounded by "{{" and "}}" if no localization is available.</returns>
        abstract protected internal string Translate_(string lang, string val);
        /// <summary>
        /// Get a localization of a non string, defaulting to ToString
        /// </summary>
        /// <param name="val">The value to localize</param>
        /// <returns>The localized string, converted by the default translat or inone is available.</returns>
        abstract public string Translate(object val);
        /// <summary>
        /// Get a localization of a non string, defaulting to ToString
        /// </summary>
        /// <param name="val">The value to localize</param>
        /// <returns>The localized string, converted by the default translat or inone is available.</returns>
        abstract protected internal string Translate_(object val);
        /// <summary>
        /// Get a localization of an internationalized string
        /// </summary>
        /// <param name="lang">The language to localize to</param>
        /// <param name="val">The string to localize</param>
        /// <returns>The localized string, or the original string surrounded by "{{" and "}}" if no localization is available.</returns>
        abstract public string Translate(string lang, object val);
        /// <summary>
        /// Get a localization of an internationalized string
        /// </summary>
        /// <param name="lang">The language to localize to</param>
        /// <param name="val">The string to localize</param>
        /// <returns>The localized string, or the original string surrounded by "{{" and "}}" if no localization is available.</returns>
        abstract protected internal string Translate_(string lang, object val);
        #endregion

        #region TextWrtier implementation
        #region properties
        /// <summary>
        /// Returns the Encoding in which the output is written.
        /// </summary>
        public override Encoding Encoding
        {
            get
            {
                if(this.m_output == null) throw new NullReferenceException("NO DEFAULT OUTPUT");
                return this.m_output.Encoding;
            }
        }
        /// <summary>
        /// Gets an object that controls formatting.
        /// </summary>
        public override IFormatProvider FormatProvider
        {
            get
            {
                if(this.m_output == null) throw new NullReferenceException("NO DEFAULT OUTPUT");
                return this.m_output.FormatProvider;
            }
        }
        /// <summary>
        /// Gets or sets the line terminator string used by the current TextWriter.
        /// </summary>
        public override string NewLine
        {
            get
            {
                if(this.m_output == null) throw new NullReferenceException("NO DEFAULT OUTPUT");
                return this.m_output.NewLine;
            }
            set
            {
                if(this.m_output == null) throw new NullReferenceException("NO DEFAULT OUTPUT");
                this.m_output.NewLine = value;
            }
        }
        #endregion

        #region methods
        /// <summary>
        /// Closes the current writer and releases any system resources associated with the enclosed writer.
        /// </summary>
        public override void Close()
        {
            if(this.m_output != null) this.m_output.Close();
        }
        /// <summary>
        /// Releases the unmanaged resources used by the TextWriter and optionally releases the managed resources.
        /// </summary>
        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources. </param>
        protected override void  Dispose(bool disposing)
        {
 	        base.Dispose(disposing);
        }
        /// <summary>
        /// Determines whether the specified Object is equal to the current Object.
        /// </summary>
        /// <param name="obj">The object to compare with the current object. </param>
        /// <returns>true if the specified Object is equal to the current Object; otherwise, false.</returns>
        public override bool Equals(Object obj)
        {
            if(this.GetType() != obj.GetType()) return false;
            Localizer o = (Localizer)obj;
            return this.m_default_localization.Equals(o.m_default_localization) &&
                   this.m_dont_translate.Equals(o.m_dont_translate) &&
                   this.m_localizations.Equals(o.m_localizations) &&
                   this.m_output.Equals(this.m_output);
        }

        public override int GetHashCode()
        {
            return this.m_default_localization.GetHashCode() ^ this.m_dont_translate.GetHashCode() ^
                   this.m_localizations.GetHashCode() ^ this.m_output.GetHashCode();
        }
        /// <summary>
        /// Clears all buffers for the current writer and causes any buffered data to be written to the underlying device.
        /// </summary>
        public override void Flush()
        {
            if(this.m_output != null) this.m_output.Flush();
        }
        /// <summary>
        /// Writes the text representation of a Boolean value to the text stream.
        /// </summary>
        /// <param name="value">he Boolean to write. </param>
        public override void Write(bool value)
        {
            this.m_output.Write(Translate(value));
        }
        /// <summary>
        /// Writes a character to the text stream.
        /// </summary>
        /// <param name="value">The character to write to the text stream. </param>
        public override void Write(char value)
        {
            this.m_output.Write(Translate(value));
        }
        /// <summary>
        /// Writes a character array to the text stream.
        /// </summary>
        /// <param name="buffer">The character array to write to the text stream. </param>
        public override void Write(char[] buffer)
        {
            this.m_output.Write(Translate(buffer));
        }
        /// <summary>
        /// Writes the text representation of a decimal value to the text stream.
        /// </summary>
        /// <param name="value">The decimal value to write. </param>
        public override void Write(decimal value)
        {
            this.m_output.Write(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of an 8-byte floating-point value to the text stream.
        /// </summary>
        /// <param name="value">The 8-byte floating-point value to write. </param>
        public override void Write(double value)
        {
            this.m_output.Write(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of a 4-byte signed integer to the text stream.
        /// </summary>
        /// <param name="value">The 4-byte signed integer to write. </param>
        public override void Write(int value)
        {
            this.m_output.Write(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of an 8-byte signed integer to the text stream.
        /// </summary>
        /// <param name="value">The 8-byte signed integer to write. </param>
        public override void Write(long value)
        {
            this.m_output.Write(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of an object to the text stream by calling ToString on that object.
        /// </summary>
        /// <param name="value">The object to write. </param>
        public override void Write(Object value)
        {
            this.m_output.Write(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of a 4-byte floating-point value to the text stream.
        /// </summary>
        /// <param name="value">The 4-byte floating-point value to write. </param>
        public override void Write(float value)
        {
            this.m_output.Write(Translate(value));
        }
        /// <summary>
        /// Writes a string to the text stream.
        /// </summary>
        /// <param name="value">The string to write. </param>
        public override void Write(string value)
        {
            this.m_output.Write(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of a 4-byte unsigned integer to the text stream.
        /// </summary>
        /// <param name="value">The 4-byte unsigned integer to write. </param>
        public override void Write(uint value)
        {
            this.m_output.Write(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of an 8-byte unsigned integer to the text stream.
        /// </summary>
        /// <param name="value">The 8-byte unsigned integer to write. </param>
        public override void Write(ulong value)
        {
            this.m_output.Write(Translate(value));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="format">The formatting string.</param>
        /// <param name="arg0">An object to write into the formatted string. (This is converted to a string)</param>
        public override void Write(string format, Object arg0)
        {
            this.m_output.Write(format, Translate(arg0));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="format">The formatting string.</param>
        /// <param name="arg">The object array to write into the formatted string. (This is converted to a string)</param>
        public override void Write(string format, params Object[] arg)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            object[] write = new object[arg.Length];
            for(int i = 0; i < arg.Length; i++) write[i] = Translate_(arg[i]);
            this.m_output.Write(format, write);
        }
        /// <summary>
        /// Writes a subarray of characters to the text stream.
        /// </summary>
        /// <param name="buffer">The character array to write data from. </param>
        /// <param name="index">Starting index in the buffer. </param>
        /// <param name="count">The number of characters to write. </param>
        public override void Write(char[] buffer, int index, int count)
        {
            char[] o = new char[count];
            for(int i = 0; i < count; i++) o[i] = buffer[index+i];
            this.m_output.Write(Translate(o));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="format">The formatting string. </param>
        /// <param name="arg0">An object to write into the formatted string. </param>
        /// <param name="arg1">An object to write into the formatted string. </param>
        public override void Write(string format, Object arg0, Object arg1)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            this.m_output.Write(format, Translate_(arg0), Translate_(arg1));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="format">The formatting string. </param>
        /// <param name="arg0">An object to write into the formatted string. </param>
        /// <param name="arg1">An object to write into the formatted string. </param>
        /// <param name="arg2">An object to write into the formatted string. </param>
        public override void Write(string format, Object arg0, Object arg1, Object arg2)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            this.m_output.Write(format, Translate_(arg0), Translate_(arg1), Translate_(arg2));
        }
        /// <summary>
        /// Not actually part o fthe TextWriter class, but important for us!
        /// </summary>
        /// <param name="output">The objects to write, each one that is not a NotLocalizable is output.</param>
        public void Write(params object[] output)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }

            foreach(object s in output)
            {
                if(s.GetType() == typeof(string))
                    this.m_output.Write((string)this.Translate_(s));
                else
                    this.m_output.Write(this.Translate_(s));
            }
        }
        /// <summary>
        /// Writes the text representation of a Boolean value to the text stream.
        /// </summary>
        /// <param name="value">he Boolean to write. </param>
        public override void WriteLine(bool value)
        {
            this.m_output.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes a character to the text stream.
        /// </summary>
        /// <param name="value">The character to write to the text stream. </param>
        public override void WriteLine(char value)
        {
            this.m_output.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes a character array to the text stream.
        /// </summary>
        /// <param name="buffer">The character array to write to the text stream. </param>
        public override void WriteLine(char[] buffer)
        {
            this.m_output.WriteLine(Translate(buffer));
        }
        /// <summary>
        /// Writes the text representation of a decimal value to the text stream.
        /// </summary>
        /// <param name="value">The decimal value to write. </param>
        public override void WriteLine(decimal value)
        {
            this.m_output.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of an 8-byte floating-point value to the text stream.
        /// </summary>
        /// <param name="value">The 8-byte floating-point value to write. </param>
        public override void WriteLine(double value)
        {
            this.m_output.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of a 4-byte signed integer to the text stream.
        /// </summary>
        /// <param name="value">The 4-byte signed integer to write. </param>
        public override void WriteLine(int value)
        {
            this.m_output.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of an 8-byte signed integer to the text stream.
        /// </summary>
        /// <param name="value">The 8-byte signed integer to write. </param>
        public override void WriteLine(long value)
        {
            this.m_output.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of an object to the text stream by calling ToString on that object.
        /// </summary>
        /// <param name="value">The object to write. </param>
        public override void WriteLine(Object value)
        {
            this.m_output.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of a 4-byte floating-point value to the text stream.
        /// </summary>
        /// <param name="value">The 4-byte floating-point value to write. </param>
        public override void WriteLine(float value)
        {
            this.m_output.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes a string to the text stream.
        /// </summary>
        /// <param name="value">The string to write. </param>
        public override void WriteLine(string value)
        {
            this.m_output.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of a 4-byte unsigned integer to the text stream.
        /// </summary>
        /// <param name="value">The 4-byte unsigned integer to write. </param>
        public override void WriteLine(uint value)
        {
            this.m_output.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of an 8-byte unsigned integer to the text stream.
        /// </summary>
        /// <param name="value">The 8-byte unsigned integer to write. </param>
        public override void WriteLine(ulong value)
        {
            this.m_output.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="format">The formatting string.</param>
        /// <param name="arg0">An object to write into the formatted string. (This is converted to a string)</param>
        public override void WriteLine(string format, Object arg0)
        {
            this.m_output.WriteLine(format, Translate(arg0));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="format">The formatting string.</param>
        /// <param name="arg">The object array to write into the formatted string. (This is converted to a string)</param>
        public override void WriteLine(string format, params Object[] arg)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            object[] write = new object[arg.Length];
            for(int i = 0; i < arg.Length; i++) write[i] = Translate_(arg[i]);
            this.m_output.WriteLine(format, write);
        }
        /// <summary>
        /// Writes a subarray of characters to the text stream.
        /// </summary>
        /// <param name="buffer">The character array to write data from. </param>
        /// <param name="index">Starting index in the buffer. </param>
        /// <param name="count">The number of characters to write. </param>
        public override void WriteLine(char[] buffer, int index, int count)
        {
            char[] o = new char[count];
            for(int i = 0; i < count; i++) o[i] = buffer[index+i];
            this.m_output.WriteLine(Translate(o));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="format">The formatting string. </param>
        /// <param name="arg0">An object to write into the formatted string. </param>
        /// <param name="arg1">An object to write into the formatted string. </param>
        public override void WriteLine(string format, Object arg0, Object arg1)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            this.m_output.WriteLine(format, Translate_(arg0), Translate_(arg1));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="format">The formatting string. </param>
        /// <param name="arg0">An object to write into the formatted string. </param>
        /// <param name="arg1">An object to write into the formatted string. </param>
        /// <param name="arg2">An object to write into the formatted string. </param>
        public override void WriteLine(string format, Object arg0, Object arg1, Object arg2)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            this.m_output.WriteLine(format, Translate_(arg0), Translate_(arg1), Translate_(arg2));
        }
        /// <summary>
        /// Not actually part o fthe TextWriter class, but important for us!
        /// </summary>
        /// <param name="output">The strings to write, each one that is not a NotLocalizable is output.</param>
        public void WriteLine(params object[] output)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }

            foreach(object s in output)
            {
                if(s.GetType() == typeof(string))
                    this.m_output.Write((string)this.Translate_(s));
                else
                    this.m_output.Write(this.Translate_(s));
            }
            this.m_output.WriteLine();
        }
        #endregion
        #endregion

        #region Abitrary-Target-TextWrtier implementation
        /// <summary>
        /// Writes the text representation of a Boolean value to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">he Boolean to write. </param>
        public void Write(TextWriter w, bool value)
        {
            w.Write((Translate(value)));
        }
        /// <summary>
        /// Writes a character to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The character to write to the text stream. </param>
        public void Write(TextWriter w, char value)
        {
            w.Write((Translate(value)));
        }
        /// <summary>
        /// Writes a character array to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="buffer">The character array to write to the text stream. </param>
        public void Write(TextWriter w, char[] buffer)
        {
            w.Write((Translate(buffer)));
        }
        /// <summary>
        /// Writes the text representation of a decimal value to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The decimal value to write. </param>
        public void Write(TextWriter w, decimal value)
        {
            w.Write((Translate(value)));
        }
        /// <summary>
        /// Writes the text representation of an 8-byte floating-point value to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The 8-byte floating-point value to write. </param>
        public void Write(TextWriter w, double value)
        {
            w.Write((Translate(value)));
        }
        /// <summary>
        /// Writes the text representation of a 4-byte signed integer to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The 4-byte signed integer to write. </param>
        public void Write(TextWriter w, int value)
        {
            w.Write((Translate(value)));
        }
        /// <summary>
        /// Writes the text representation of an 8-byte signed integer to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The 8-byte signed integer to write. </param>
        public void Write(TextWriter w, long value)
        {
            w.Write((Translate(value)));
        }
        /// <summary>
        /// Writes the text representation of an object to the text stream by calling ToString on that object.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The object to write. </param>
        public void Write(TextWriter w, Object value)
        {
            w.Write((Translate(value)));
        }
        /// <summary>
        /// Writes the text representation of a 4-byte floating-point value to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The 4-byte floating-point value to write. </param>
        public void Write(TextWriter w, float value)
        {
            w.Write((Translate(value)));
        }
        /// <summary>
        /// Writes a string to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The string to write. </param>
        public void Write(TextWriter w, string value)
        {
            w.Write((Translate(value)));
        }
        /// <summary>
        /// Writes the text representation of a 4-byte unsigned integer to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The 4-byte unsigned integer to write. </param>
        public void Write(TextWriter w, uint value)
        {
            w.Write((Translate(value)));
        }
        /// <summary>
        /// Writes the text representation of an 8-byte unsigned integer to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The 8-byte unsigned integer to write. </param>
        public void Write(TextWriter w, ulong value)
        {
            w.Write((Translate(value)));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="format">The formatting string.</param>
        /// <param name="arg0">An object to write into the formatted string. (This is converted to a string)</param>
        public void Write(TextWriter w, string format, Object arg0)
        {
            w.Write(format, Translate(arg0));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="format">The formatting string.</param>
        /// <param name="arg">The object array to write into the formatted string. (This is converted to a string)</param>
        public void Write(TextWriter w, string format, params Object[] arg)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            object[] write = new object[arg.Length];
            for(int i = 0; i < arg.Length; i++) write[i] = Translate_(arg[i]);
            w.Write(format, write);
        }
        /// <summary>
        /// Writes a subarray of characters to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="buffer">The character array to write data from. </param>
        /// <param name="index">Starting index in the buffer. </param>
        /// <param name="count">The number of characters to write. </param>
        public void Write(TextWriter w, char[] buffer, int index, int count)
        {
            char[] o = new char[count];
            for(int i = 0; i < count; i++) o[i] = buffer[index + i];
            w.Write(Translate(o));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="format">The formatting string. </param>
        /// <param name="arg0">An object to write into the formatted string. </param>
        /// <param name="arg1">An object to write into the formatted string. </param>
        public void Write(TextWriter w, string format, Object arg0, Object arg1)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            w.Write(format, Translate_(arg0), Translate_(arg1));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="format">The formatting string. </param>
        /// <param name="arg0">An object to write into the formatted string. </param>
        /// <param name="arg1">An object to write into the formatted string. </param>
        /// <param name="arg2">An object to write into the formatted string. </param>
        public void Write(TextWriter w, string format, Object arg0, Object arg1, Object arg2)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            w.Write(format, Translate_(arg0), Translate_(arg1), Translate_(arg2));
        }
        /// <summary>
        /// Not actually part o fthe TextWriter class, but important for us!
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="output">The objects to write, each one that is not a NotLocalizable is output.</param>
        public void Write(TextWriter w, params object[] output)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }

            foreach(object s in output)
            {
                if(s.GetType() == typeof(string))
                    w.Write((string)this.Translate_(s));
                else
                    w.Write(this.Translate_(s));
            }
        }
        /// <summary>
        /// Writes the text representation of a Boolean value to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">he Boolean to write. </param>
        public void WriteLine(TextWriter w, bool value)
        {
            w.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes a character to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The character to write to the text stream. </param>
        public void WriteLine(TextWriter w, char value)
        {
            w.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes a character array to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="buffer">The character array to write to the text stream. </param>
        public void WriteLine(TextWriter w, char[] buffer)
        {
            w.WriteLine(Translate(buffer));
        }
        /// <summary>
        /// Writes the text representation of a decimal value to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The decimal value to write. </param>
        public void WriteLine(TextWriter w, decimal value)
        {
            w.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of an 8-byte floating-point value to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The 8-byte floating-point value to write. </param>
        public void WriteLine(TextWriter w, double value)
        {
            w.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of a 4-byte signed integer to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The 4-byte signed integer to write. </param>
        public void WriteLine(TextWriter w, int value)
        {
            w.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of an 8-byte signed integer to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The 8-byte signed integer to write. </param>
        public void WriteLine(TextWriter w, long value)
        {
            w.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of an object to the text stream by calling ToString on that object.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The object to write. </param>
        public void WriteLine(TextWriter w, Object value)
        {
            w.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of a 4-byte floating-point value to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The 4-byte floating-point value to write. </param>
        public void WriteLine(TextWriter w, float value)
        {
            w.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes a string to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The string to write. </param>
        public void WriteLine(TextWriter w, string value)
        {
            w.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of a 4-byte unsigned integer to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The 4-byte unsigned integer to write. </param>
        public void WriteLine(TextWriter w, uint value)
        {
            w.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes the text representation of an 8-byte unsigned integer to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="value">The 8-byte unsigned integer to write. </param>
        public void WriteLine(TextWriter w, ulong value)
        {
            w.WriteLine(Translate(value));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="format">The formatting string.</param>
        /// <param name="arg0">An object to write into the formatted string. (This is converted to a string)</param>
        public void WriteLine(TextWriter w, string format, Object arg0)
        {
            w.WriteLine(format, Translate(arg0));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="format">The formatting string.</param>
        /// <param name="arg">The object array to write into the formatted string. (This is converted to a string)</param>
        public void WriteLine(TextWriter w, string format, params Object[] arg)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            object[] write = new object[arg.Length];
            for(int i = 0; i < arg.Length; i++) write[i] = Translate_(arg[i]);
            w.WriteLine(format, write);
        }
        /// <summary>
        /// Writes a subarray of characters to the text stream.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="buffer">The character array to write data from. </param>
        /// <param name="index">Starting index in the buffer. </param>
        /// <param name="count">The number of characters to write. </param>
        public void WriteLine(TextWriter w, char[] buffer, int index, int count)
        {
            char[] o = new char[count];
            for(int i = 0; i < count; i++) o[i] = buffer[index + i];
            w.WriteLine(Translate(o));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="format">The formatting string. </param>
        /// <param name="arg0">An object to write into the formatted string. </param>
        /// <param name="arg1">An object to write into the formatted string. </param>
        public void WriteLine(TextWriter w, string format, Object arg0, Object arg1)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            w.WriteLine(format, Translate_(arg0), Translate_(arg1));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="format">The formatting string. </param>
        /// <param name="arg0">An object to write into the formatted string. </param>
        /// <param name="arg1">An object to write into the formatted string. </param>
        /// <param name="arg2">An object to write into the formatted string. </param>
        public void WriteLine(TextWriter w, string format, Object arg0, Object arg1, Object arg2)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            w.WriteLine(format, Translate_(arg0), Translate_(arg1), Translate_(arg2));
        }
        /// <summary>
        /// Not actually part o fthe TextWriter class, but important for us!
        /// </summary>
        /// <param name="w">The text writer to send output to.</param>
        /// <param name="output">The strings to write, each one that is not a NotLocalizable is output.</param>
        public void WriteLine(TextWriter w, params object[] output)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }

            foreach(object s in output)
            {
                if(s.GetType() == typeof(string))
                    w.Write((string)this.Translate_(s));
                else
                    w.Write(this.Translate_(s));
            }
            w.WriteLine();
        }
        #endregion

        #region WinForms TextWriter-like implementation
        /// <summary>
        /// Writes the text representation of a Boolean value to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">he Boolean to write. </param>
        public void Write(Control c, bool value)
        {
            c.Text += Translate(value);
        }
        /// <summary>
        /// Writes a character to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The character to write to the text stream. </param>
        public void Write(Control c, char value)
        {
            c.Text += Translate(value);
        }
        /// <summary>
        /// Writes a character array to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="buffer">The character array to write to the text stream. </param>
        public void Write(Control c, char[] buffer)
        {
            c.Text += Translate(buffer);
        }
        /// <summary>
        /// Writes the text representation of a decimal value to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The decimal value to write. </param>
        public void Write(Control c, decimal value)
        {
            c.Text += Translate(value);
        }
        /// <summary>
        /// Writes the text representation of an 8-byte floating-point value to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 8-byte floating-point value to write. </param>
        public void Write(Control c, double value)
        {
            c.Text += Translate(value);
        }
        /// <summary>
        /// Writes the text representation of a 4-byte signed integer to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 4-byte signed integer to write. </param>
        public void Write(Control c, int value)
        {
            c.Text += Translate(value);
        }
        /// <summary>
        /// Writes the text representation of an 8-byte signed integer to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 8-byte signed integer to write. </param>
        public void Write(Control c, long value)
        {
            c.Text += Translate(value);
        }
        /// <summary>
        /// Writes the text representation of an object to the text stream by calling ToString on that object.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The object to write. </param>
        public void Write(Control c, Object value)
        {
            c.Text += Translate(value);
        }
        /// <summary>
        /// Writes the text representation of a 4-byte floating-point value to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 4-byte floating-point value to write. </param>
        public void Write(Control c, float value)
        {
            c.Text += Translate(value);
        }
        /// <summary>
        /// Writes a string to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The string to write. </param>
        public void Write(Control c, string value)
        {
            c.Text += Translate(value);
        }
        /// <summary>
        /// Writes the text representation of a 4-byte unsigned integer to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 4-byte unsigned integer to write. </param>
        public void Write(Control c, uint value)
        {
            c.Text += Translate(value);
        }
        /// <summary>
        /// Writes the text representation of an 8-byte unsigned integer to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 8-byte unsigned integer to write. </param>
        public void Write(Control c, ulong value)
        {
            c.Text += Translate(value);
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="format">The formatting string.</param>
        /// <param name="arg0">An object to write into the formatted string. (This is converted to a string)</param>
        public void Write(Control c, string format, Object arg0)
        {
            c.Text += String.Format(format, Translate(arg0));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="format">The formatting string.</param>
        /// <param name="arg">The object array to write into the formatted string. (This is converted to a string)</param>
        public void Write(Control c, string format, params Object[] arg)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            object[] write = new object[arg.Length];
            for(int i = 0; i < arg.Length; i++) write[i] = Translate_(arg[i]);
            c.Text += String.Format(format, write);
        }
        /// <summary>
        /// Writes a subarray of characters to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="buffer">The character array to write data from. </param>
        /// <param name="index">Starting index in the buffer. </param>
        /// <param name="count">The number of characters to write. </param>
        public void Write(Control c, char[] buffer, int index, int count)
        {
            char[] o = new char[count];
            for(int i = 0; i < count; i++) o[i] = buffer[index+i];
            c.Text += String.Format(Translate(o));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="format">The formatting string. </param>
        /// <param name="arg0">An object to write into the formatted string. </param>
        /// <param name="arg1">An object to write into the formatted string. </param>
        public void Write(Control c, string format, Object arg0, Object arg1)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            c.Text += String.Format(format, Translate_(arg0), Translate_(arg1));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="format">The formatting string. </param>
        /// <param name="arg0">An object to write into the formatted string. </param>
        /// <param name="arg1">An object to write into the formatted string. </param>
        /// <param name="arg2">An object to write into the formatted string. </param>
        public void Write(Control c, string format, Object arg0, Object arg1, Object arg2)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            c.Text += String.Format(format, Translate_(arg0), Translate_(arg1), Translate_(arg2));
        }
        /// <summary>
        /// Not actually part o fthe TextWriter class, but important for us!
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="output">The objects to write, each one that is not a NotLocalizable is output.</param>
        public void Write(Control c, params object[] output)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            if(c.Text == null) c.Text = "";
            foreach(object s in output)
            {
                if(s.GetType() == typeof(string))
                    c.Text += this.Translate_(s);
                else
                    c.Text += this.Translate_(s);
            }
        }

        /// <summary>
        /// Writes the text representation of a Boolean value to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">he Boolean to write. </param>
        public void WriteLine(Control c, bool value)
        {
            c.Text += Translate(value) + "\n";
        }
        /// <summary>
        /// Writes a character to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The character to write to the text stream. </param>
        public void WriteLine(Control c, char value)
        {
            c.Text += Translate(value) + "\n";
        }
        /// <summary>
        /// Writes a character array to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="buffer">The character array to write to the text stream. </param>
        public void WriteLine(Control c, char[] buffer)
        {
            c.Text += Translate(buffer) + "\n";
        }
        /// <summary>
        /// Writes the text representation of a decimal value to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The decimal value to write. </param>
        public void WriteLine(Control c, decimal value)
        {
            c.Text += Translate(value) + "\n";
        }
        /// <summary>
        /// Writes the text representation of an 8-byte floating-point value to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 8-byte floating-point value to write. </param>
        public void WriteLine(Control c, double value)
        {
            c.Text += Translate(value) + "\n";
        }
        /// <summary>
        /// Writes the text representation of a 4-byte signed integer to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 4-byte signed integer to write. </param>
        public void WriteLine(Control c, int value)
        {
            c.Text += Translate(value) + "\n";
        }
        /// <summary>
        /// Writes the text representation of an 8-byte signed integer to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 8-byte signed integer to write. </param>
        public void WriteLine(Control c, long value)
        {
            c.Text += Translate(value) + "\n";
        }
        /// <summary>
        /// Writes the text representation of an object to the text stream by calling ToString on that object.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The object to write. </param>
        public void WriteLine(Control c, Object value)
        {
            c.Text += Translate(value) + "\n";
        }
        /// <summary>
        /// Writes the text representation of a 4-byte floating-point value to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 4-byte floating-point value to write. </param>
        public void WriteLine(Control c, float value)
        {
            c.Text += Translate(value) + "\n";
        }
        /// <summary>
        /// Writes a string to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The string to write. </param>
        public void WriteLine(Control c, string value)
        {
            c.Text += Translate(value) + "\n";
        }
        /// <summary>
        /// Writes the text representation of a 4-byte unsigned integer to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 4-byte unsigned integer to write. </param>
        public void WriteLine(Control c, uint value)
        {
            c.Text += Translate(value) + "\n";
        }
        /// <summary>
        /// Writes the text representation of an 8-byte unsigned integer to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 8-byte unsigned integer to write. </param>
        public void WriteLine(Control c, ulong value)
        {
            c.Text += Translate(value) + "\n";
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="format">The formatting string.</param>
        /// <param name="arg0">An object to write into the formatted string. (This is converted to a string)</param>
        public void WriteLine(Control c, string format, Object arg0)
        {
            c.Text += String.Format(format, Translate(arg0)) + "\n";
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="format">The formatting string.</param>
        /// <param name="arg">The object array to write into the formatted string. (This is converted to a string)</param>
        public void WriteLine(Control c, string format, params Object[] arg)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            object[] write = new object[arg.Length];
            for(int i = 0; i < arg.Length; i++) write[i] = Translate_(arg[i]);
            c.Text += String.Format(format, write) + "\n";
        }
        /// <summary>
        /// Writes a subarray of characters to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="buffer">The character array to write data from. </param>
        /// <param name="index">Starting index in the buffer. </param>
        /// <param name="count">The number of characters to write. </param>
        public void WriteLine(Control c, char[] buffer, int index, int count)
        {
            char[] o = new char[count];
            for(int i = 0; i < count; i++) o[i] = buffer[index+i];
            c.Text += String.Format(Translate(o)) + "\n";
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="format">The formatting string. </param>
        /// <param name="arg0">An object to write into the formatted string. </param>
        /// <param name="arg1">An object to write into the formatted string. </param>
        public void WriteLine(Control c, string format, Object arg0, Object arg1)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            c.Text += String.Format(format, Translate_(arg0), Translate_(arg1))+"\n";
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="format">The formatting string. </param>
        /// <param name="arg0">An object to write into the formatted string. </param>
        /// <param name="arg1">An object to write into the formatted string. </param>
        /// <param name="arg2">An object to write into the formatted string. </param>
        public void WriteLine(Control c, string format, Object arg0, Object arg1, Object arg2)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            c.Text += String.Format(format, Translate_(arg0), Translate_(arg1), Translate_(arg2))+"\n";
        }
        /// <summary>
        /// Not actually part o fthe TextWriter class, but important for us!
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="output">The strings to write, each one that is not a NotLocalizable is output.</param>
        public void WriteLine(Control c, params object[] output)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            if(c.Text == null) c.Text = "";
            foreach(object s in output)
            {
                if(s.GetType() == typeof(string))
                    c.Text += this.Translate_(s);
                else
                    c.Text += this.Translate_(s);
            }
            c.Text += "\n";
        }
        #endregion

        #region Generic Object TextWriter-like implementation
        /// <summary>
        /// Attempt to write to the target. This is done by looking for static functions or properites
        /// in the target's type. It writes to the first public matching member found. The targets
        /// checked (in order) are: Method 'Write(string)', Method 'Append(string)',
        /// Property 'string Text', Property 'string Content', Property 'object Content'.
        /// If the target is a property of type 'object', it will check if the returned value is a string,
        /// and if so, the text will be appended. If not, the value will be set to "", and then the new
        /// text appended. BE CAREFUL, this can cause an error
        /// </summary>
        /// <param name="o">The object to place the translated text into</param>
        /// <param name="s">The string of translated text</param>
        abstract public void WriteToGeneric(object o, string s);

        /// <summary>
        /// Writes the text representation of a Boolean value to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">he Boolean to write. </param>
        public void WriteGeneric(object c, bool value)
        {
            WriteToGeneric(c, Translate(value));
        }
        /// <summary>
        /// Writes a character to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The character to write to the text stream. </param>
        public void WriteGeneric(object c, char value)
        {
            WriteToGeneric(c, Translate(value));
        }
        /// <summary>
        /// Writes a character array to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="buffer">The character array to write to the text stream. </param>
        public void WriteGeneric(object c, char[] buffer)
        {
            WriteToGeneric(c, Translate(buffer));
        }
        /// <summary>
        /// Writes the text representation of a decimal value to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The decimal value to write. </param>
        public void WriteGeneric(object c, decimal value)
        {
            WriteToGeneric(c, Translate(value));
        }
        /// <summary>
        /// Writes the text representation of an 8-byte floating-point value to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 8-byte floating-point value to write. </param>
        public void WriteGeneric(object c, double value)
        {
            WriteToGeneric(c, Translate(value));
        }
        /// <summary>
        /// Writes the text representation of a 4-byte signed integer to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 4-byte signed integer to write. </param>
        public void WriteGeneric(object c, int value)
        {
            WriteToGeneric(c, Translate(value));
        }
        /// <summary>
        /// Writes the text representation of an 8-byte signed integer to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 8-byte signed integer to write. </param>
        public void WriteGeneric(object c, long value)
        {
            WriteToGeneric(c, Translate(value));
        }
        /// <summary>
        /// Writes the text representation of an object to the text stream by calling ToString on that object.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The object to write. </param>
        public void WriteGeneric(object c, Object value)
        {
            WriteToGeneric(c, Translate(value));
        }
        /// <summary>
        /// Writes the text representation of a 4-byte floating-point value to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 4-byte floating-point value to write. </param>
        public void WriteGeneric(object c, float value)
        {
            WriteToGeneric(c, Translate(value));
        }
        /// <summary>
        /// Writes a string to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The string to write. </param>
        public void WriteGeneric(object c, string value)
        {
            WriteToGeneric(c, Translate(value));
        }
        /// <summary>
        /// Writes the text representation of a 4-byte unsigned integer to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 4-byte unsigned integer to write. </param>
        public void WriteGeneric(object c, uint value)
        {
            WriteToGeneric(c, Translate(value));
        }
        /// <summary>
        /// Writes the text representation of an 8-byte unsigned integer to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 8-byte unsigned integer to write. </param>
        public void WriteGeneric(object c, ulong value)
        {
            WriteToGeneric(c, Translate(value));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="format">The formatting string.</param>
        /// <param name="arg0">An object to write into the formatted string. (This is converted to a string)</param>
        public void WriteGeneric(object c, string format, Object arg0)
        {
            WriteToGeneric(c, String.Format(format, Translate(arg0)));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="format">The formatting string.</param>
        /// <param name="arg">The object array to write into the formatted string. (This is converted to a string)</param>
        public void WriteGeneric(object c, string format, params Object[] arg)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            object[] write = new object[arg.Length];
            for(int i = 0; i < arg.Length; i++) 
                write[i] = Translate_(arg[i]);
            WriteToGeneric(c, String.Format(format, write));
        }
        /// <summary>
        /// Writes a subarray of characters to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="buffer">The character array to write data from. </param>
        /// <param name="index">Starting index in the buffer. </param>
        /// <param name="count">The number of characters to write. </param>
        public void WriteGeneric(object c, char[] buffer, int index, int count)
        {
            char[] o = new char[count];
            for(int i = 0; i < count; i++) o[i] = buffer[index + i];
            WriteToGeneric(c, Translate(o));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="format">The formatting string. </param>
        /// <param name="arg0">An object to write into the formatted string. </param>
        /// <param name="arg1">An object to write into the formatted string. </param>
        public void WriteGeneric(object c, string format, Object arg0, Object arg1)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            WriteToGeneric(c, String.Format(format, Translate_(arg0), Translate_(arg1)));
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="format">The formatting string. </param>
        /// <param name="arg0">An object to write into the formatted string. </param>
        /// <param name="arg1">An object to write into the formatted string. </param>
        /// <param name="arg2">An object to write into the formatted string. </param>
        public void WriteGeneric(object c, string format, Object arg0, Object arg1, Object arg2)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            WriteToGeneric(c, String.Format(format, Translate_(arg0), Translate_(arg1), Translate_(arg2)));
        }
        /// <summary>
        /// Not actually part o fthe TextWriter class, but important for us!
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="output">The objects to write, each one that is not a NotLocalizable is output.</param>
        public void WriteGeneric(object c, params object[] output)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            foreach(object s in output)
            {
                if(s.GetType() == typeof(string))
                    WriteToGeneric(c, this.Translate_(s));
                else
                    WriteToGeneric(c, this.Translate_(s));
            }
        }

        /// <summary>
        /// Writes the text representation of a Boolean value to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">he Boolean to write. </param>
        public void WriteLineGeneric(object c, bool value)
        {
            WriteToGeneric(c, Translate(value) + "\n");
        }
        /// <summary>
        /// Writes a character to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The character to write to the text stream. </param>
        public void WriteLineGeneric(object c, char value)
        {
            WriteToGeneric(c, Translate(value) + "\n");
        }
        /// <summary>
        /// Writes a character array to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="buffer">The character array to write to the text stream. </param>
        public void WriteLineGeneric(object c, char[] buffer)
        {
            WriteToGeneric(c, Translate(buffer) + "\n");
        }
        /// <summary>
        /// Writes the text representation of a decimal value to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The decimal value to write. </param>
        public void WriteLineGeneric(object c, decimal value)
        {
            WriteToGeneric(c, Translate(value) + "\n");
        }
        /// <summary>
        /// Writes the text representation of an 8-byte floating-point value to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 8-byte floating-point value to write. </param>
        public void WriteLineGeneric(object c, double value)
        {
            WriteToGeneric(c, Translate(value) + "\n");
        }
        /// <summary>
        /// Writes the text representation of a 4-byte signed integer to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 4-byte signed integer to write. </param>
        public void WriteLineGeneric(object c, int value)
        {
            WriteToGeneric(c, Translate(value) + "\n");
        }
        /// <summary>
        /// Writes the text representation of an 8-byte signed integer to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 8-byte signed integer to write. </param>
        public void WriteLineGeneric(object c, long value)
        {
            WriteToGeneric(c, Translate(value) + "\n");
        }
        /// <summary>
        /// Writes the text representation of an object to the text stream by calling ToString on that object.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The object to write. </param>
        public void WriteLineGeneric(object c, Object value)
        {
            WriteToGeneric(c, Translate(value) + "\n");
        }
        /// <summary>
        /// Writes the text representation of a 4-byte floating-point value to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 4-byte floating-point value to write. </param>
        public void WriteLineGeneric(object c, float value)
        {
            WriteToGeneric(c, Translate(value) + "\n");
        }
        /// <summary>
        /// Writes a string to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The string to write. </param>
        public void WriteLineGeneric(object c, string value)
        {
            WriteToGeneric(c, Translate(value) + "\n");
        }
        /// <summary>
        /// Writes the text representation of a 4-byte unsigned integer to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 4-byte unsigned integer to write. </param>
        public void WriteLineGeneric(object c, uint value)
        {
            WriteToGeneric(c, Translate(value) + "\n");
        }
        /// <summary>
        /// Writes the text representation of an 8-byte unsigned integer to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="value">The 8-byte unsigned integer to write. </param>
        public void WriteLineGeneric(object c, ulong value)
        {
            WriteToGeneric(c, Translate(value) + "\n");
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="format">The formatting string.</param>
        /// <param name="arg0">An object to write into the formatted string. (This is converted to a string)</param>
        public void WriteLineGeneric(object c, string format, Object arg0)
        {
            WriteToGeneric(c, String.Format(format, Translate(arg0)) + "\n");
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="format">The formatting string.</param>
        /// <param name="arg">The object array to write into the formatted string. (This is converted to a string)</param>
        public void WriteLineGeneric(object c, string format, params Object[] arg)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            object[] write = new object[arg.Length];
            for(int i = 0; i < arg.Length; i++) write[i] = Translate_(arg[i]);
            WriteToGeneric(c, String.Format(format, write) + "\n");
        }
        /// <summary>
        /// Writes a subarray of characters to the text stream.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="buffer">The character array to write data from. </param>
        /// <param name="index">Starting index in the buffer. </param>
        /// <param name="count">The number of characters to write. </param>
        public void WriteLine(object c, char[] buffer, int index, int count)
        {
            char[] o = new char[count];
            for(int i = 0; i < count; i++) o[i] = buffer[index + i];
            WriteToGeneric(c, String.Format(Translate(o)) + "\n");
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="format">The formatting string. </param>
        /// <param name="arg0">An object to write into the formatted string. </param>
        /// <param name="arg1">An object to write into the formatted string. </param>
        public void WriteLineGeneric(object c, string format, Object arg0, Object arg1)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            WriteToGeneric(c, String.Format(format, Translate_(arg0), Translate_(arg1)) + "\n");
        }
        /// <summary>
        /// Writes out a formatted string, using the same semantics as String.Format.
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="format">The formatting string. </param>
        /// <param name="arg0">An object to write into the formatted string. </param>
        /// <param name="arg1">An object to write into the formatted string. </param>
        /// <param name="arg2">An object to write into the formatted string. </param>
        public void WriteLineGeneric(object c, string format, Object arg0, Object arg1, Object arg2)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            WriteToGeneric(c, String.Format(String.Format(format, Translate_(arg0), Translate_(arg1), Translate_(arg2)) + "\n"));
        }
        /// <summary>
        /// Not actually part o fthe TextWriter class, but important for us!
        /// </summary>
        /// <param name="c">The windows control, the translated replaces the Text field.</param>
        /// <param name="output">The strings to write, each one that is not a NotLocalizable is output.</param>
        public void WriteLineGeneric(object c, params object[] output)
        {
            if(this.m_default_localization == null)
            {
                //we don't localize this - there is no default
                throw new NullReferenceException("Cannot localize, no default localization set.");
            }
            foreach(object s in output)
            {
                WriteToGeneric(c, this.Translate_(s));
            }
            WriteToGeneric(c, "\n");
        }
        #endregion
    }
}
